﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

using sfsocket.corelib.common;
using sfsocket.corelib.message;

namespace sfsocket.corelib.connection.client
{
    public class ChatClientConnection
    {
        protected TcpClient m_TcpClient = null;
        protected IPEndPoint m_RemoteClientEP = null;
        protected bool m_IsConnected = false;

        // inner thread
        protected ChatClientReadThread m_ReadThread;
        protected ChatClientIdleThread m_IdleThread;
        protected Thread m_MessageThread;

        // for idle ping thread 
        protected int m_IdleWorkerInterval = 60;
        protected int m_PingInterval = 60;
        protected int m_PingTimeout = 30;
        protected DateTime m_LastPingSent;
        protected DateTime m_LastPongReceived;
        protected TimeSpan m_Lag;

        //---------------------------------------------------------------------
        // Events
        //---------------------------------------------------------------------
        public event EventHandler OnDisconnecting;
        public event EventHandler OnDisconnected;
        public event EventHandler OnConnectionError;
        public event EventHandler OnConnecting;
        public event EventHandler OnConnected;

        public event MsgEventHandler OnMessage;

        //---------------------------------------------------------------------
        // property
        //---------------------------------------------------------------------
        public bool IsConnected
        {
            get { return m_IsConnected; }
            set { m_IsConnected = value; }
        }

        public IPEndPoint RemoteClientEP
        {
            get { return m_RemoteClientEP; }
            set { m_RemoteClientEP = value; }
        }

        public int IdleWorkerInterval
        {
            get { return m_IdleWorkerInterval; }
            set { m_IdleWorkerInterval = value; }
        }

        public int PingInterval
        {
            get { return m_PingInterval; }
            set { m_PingInterval = value; }
        }

        public int PingTimeout
        {
            get { return m_PingTimeout; }
            set { m_PingTimeout = value; }
        }

        public DateTime LastPingSent
        {
            get { return m_LastPingSent; }
            set { m_LastPingSent = value; }
        }

        public DateTime LastPongReceived
        {
            get { return m_LastPongReceived; }
            set { m_LastPongReceived = value; }
        }

        public TimeSpan Lag
        {
            get { return m_Lag; }
            set { m_Lag = value; }
        }

        //---------------------------------------------------------------------
        // Constructor
        //---------------------------------------------------------------------
        public ChatClientConnection()
        {

        }


        //---------------------------------------------------------------------
        // Methods 
        //---------------------------------------------------------------------

        /// <summary>
        /// normally connection in client
        /// </summary>
        public void Connect(string _hostname, int _port)
        {
            m_TcpClient = new TcpClient();
            m_TcpClient.NoDelay = true;
            m_TcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
            m_TcpClient.Connect(_hostname, _port);
            if (m_TcpClient.Connected)
            {
                m_IsConnected = true;
                m_RemoteClientEP = (IPEndPoint)m_TcpClient.Client.RemoteEndPoint;

                if (this.OnConnected != null) {
                    OnConnected(this, EventArgs.Empty);
                }

                if (m_ReadThread == null)
                    m_ReadThread = new ChatClientReadThread(this);
                if (!m_ReadThread.IsStarted)
                    m_ReadThread.Start();

                //if (m_IdleThread == null)
                //    m_IdleThread = new ChatClientIdleThread(this);
                //if (!m_IdleThread.IsStarted)
                //    m_IdleThread.Start();
                
                m_MessageThread = new Thread(new ThreadStart(ProcessServerMessageQueue));
                m_MessageThread.Start();
            }
        }

        public virtual void Disconnect()
        {
            if (m_TcpClient.Connected)
            {
                m_TcpClient.Close();
                if (OnDisconnected != null)
                {
                    OnDisconnected(this, EventArgs.Empty);
                }
            }

            if (m_ReadThread != null)
                m_ReadThread.Stop();
            
            if (m_IdleThread != null)
                m_IdleThread.Stop();
            
            if (m_MessageThread != null)
                m_MessageThread.Abort();

            Console.WriteLine("[{0,3}] - {1} : Disconnect",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"));
        }


        public NetworkStream GetStream()
        {
            if (m_TcpClient != null)
            {
                return m_TcpClient.GetStream();
            }
            return null;
        }

        //---------------------------------------------------------------------
        //  
        //---------------------------------------------------------------------
        public int SendMsg(BaseMessage msg)
        {
            lock (m_TcpClient.GetStream())
            {
                NetworkStream ns = GetStream();
                ns.Write(msg.GetBytes(), 0, msg.GetBytes().Length);
                ns.Flush();
                // TraceMsg("Sending " + Enum.GetName(typeof(MessageTypeEnum), msg.MessageType) + " msg");
                return 1;
            }


        }
        //---------------------------------------------------------------------
        // Process Message  
        //---------------------------------------------------------------------
        public virtual void ProcessServerMessageQueue()
        {
            TraceMsg("Pool and Dequeuing Message");
            while (true)
            {
                if (m_ReadThread.Queue.Count > 0)
                {
                    RawMessage msg = (RawMessage)m_ReadThread.Queue.Dequeue();
                    switch (((RawMessage)msg).MessageType)
                    {
                        case (int)MessageTypeEnum.PING:
                            PingMessage pingmsg = new PingMessage();
                            //OnPing(pingmsg);
                            break;
                        case (int)MessageTypeEnum.BROADCAST:
                            ChatMessage chatmsg = ChatMessage.Parse(msg.GetBytes());
                            if (OnMessage != null)
                            {
                                OnMessage(this, new MessageEventArgs("Receive msg: " + chatmsg.Chatmsg));
                            }
                            break;
                        case (int)MessageTypeEnum.FILEDATE:
                            TraceMsg("receiv filedata");
                            FileDataMessage fmsg = FileDataMessage.Parse(msg.GetBytes());
                            if (fmsg != null)
                            {
                                //fmsg.Dump();
                                //OnFileDataReceive(fmsg);
                            }
                            break;
                        defalut:
                            TraceMsg("Unexpected Message Received");
                            ;
                    }
                    //msg.Dump();
                }
                Thread.Sleep(100);
            }
        }

        //---------------------------------------------------------------------
        //  
        //---------------------------------------------------------------------
        private void OnReceiveCallback(IAsyncResult IAR)
        {

        }
        public void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                    Thread.CurrentThread.ManagedThreadId,
                    DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        
    }

    public class ChatClientConnectionUnitTest
    {
        public static void test()
        {
            ChatClientConnection con = new ChatClientConnection();
            con.Connect("127.0.0.1", 8080);
            ChatMessage msg = new ChatMessage("hello");
            con.SendMsg(msg);
        }
    }
}
